package com.ssb.util.sign;


import java.io.FileInputStream;
import java.io.IOException;
import java.math.BigInteger;
import java.security.*;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.interfaces.RSAPrivateCrtKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.RSAPrivateCrtKeySpec;
import java.security.spec.RSAPublicKeySpec;
import java.util.Enumeration;

/**
 * 证书和 Key 的操作帮助类
 * @author Administrator
 *
 */
public class CertAndKeyHelper {
    /**
     * Java密钥库(Java 密钥库，JKS)KEY_STORE
     */
    //public static final String KEY_STORE_TYPE = "JKS";
    public static final String X509 = "X.509";
    public static final String KEY_STORE_TYPE = "PKCS12";

    /**
     *
     * @param keyStorePath 密钥库存储路径
     * @param alias
     * @param password 密钥库密码
     * @return
     * @throws IOException
     * @throws CertificateException
     * @throws NoSuchAlgorithmException
     * @throws KeyStoreException
     * @throws UnrecoverableKeyException
     */
    public static PrivateKey getPrivateKey(String keyStorePath, String alias, String password)
            throws KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException, UnrecoverableKeyException
    {
        KeyStore keyStore = getKeyStore(keyStorePath, password);
        PrivateKey privateKey = (PrivateKey) keyStore.getKey(alias, password.toCharArray());
        return privateKey;
    }

    /**
     * 获取证书别名
     * @param keyStore
     * @return
     * @throws KeyStoreException
     */
    public static String getKeyAlias(KeyStore keyStore)
            throws KeyStoreException
    {
        String keyAlias = "";
        Enumeration<String> enums = keyStore.aliases();
        while (enums.hasMoreElements()) {
            keyAlias = enums.nextElement();
        }
        return keyAlias;
    }
    /**
     * <p>
     * 获得密钥库
     * </p>
     *
     * @param keyStorePath 密钥库存储路径
     * @param password 密钥库密码
     * @return
     * @throws KeyStoreException
     * @throws IOException
     * @throws CertificateException
     * @throws NoSuchAlgorithmException
     */
    public static KeyStore getKeyStore(String keyStorePath, String password)
            throws KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException
    {
        FileInputStream fis = null;
        try
        {
            fis = new FileInputStream(keyStorePath);
            KeyStore keyStore = KeyStore.getInstance(KEY_STORE_TYPE);
            keyStore.load(fis, password.toCharArray());
            return keyStore;
        }
        finally
        {
            if(fis != null)
                fis.close();
        }
    }

    /**
     * <p>
     * 根据证书获得公钥
     * </p>
     *
     * @param certificatePath 证书存储路径
     * @return
     * @throws IOException
     * @throws CertificateException
     */
    public static PublicKey getPublicKey(String certificatePath)
            throws CertificateException, IOException
    {
        Certificate certificate = getCertificate(certificatePath);
        PublicKey publicKey = certificate.getPublicKey();
        return publicKey;
    }

    /**
     * <p>
     * 获得证书
     * </p>
     *
     * @param certificatePath 证书存储路径
     * @return
     * @throws CertificateException
     * @throws IOException
     */
    public static Certificate getCertificate(String certificatePath)
            throws CertificateException, IOException
    {
        CertificateFactory certificateFactory = CertificateFactory.getInstance(X509);
        FileInputStream fis = null;
        try
        {
            fis = new FileInputStream(certificatePath);
            Certificate certificate = certificateFactory.generateCertificate(fis);
            return certificate;
        }
        finally
        {
            if(fis != null)
                fis.close();
        }
    }

    /**
     * <p>
     * 根据密钥库获得证书
     * </p>
     *
     * @param keyStorePath 密钥库存储路径
     * @param alias 密钥库别名
     * @param password 密钥库密码
     * @return
     * @throws IOException
     * @throws CertificateException
     * @throws NoSuchAlgorithmException
     * @throws KeyStoreException
     */
    public static Certificate getCertificate(String keyStorePath, String alias, String password)
            throws KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException
    {
        KeyStore keyStore = getKeyStore(keyStorePath, password);
        Certificate certificate = keyStore.getCertificate(alias);
        return certificate;
    }
    /**
     * 将 RSA 公钥数据转换为 Xml 格式的字符串。
     * @param key 公钥
     * @return Xml 格式的字符串
     */
    public static String encodePublicKeyToXml(PublicKey key)
    {
        if (!RSAPublicKey.class.isInstance(key)) {
            return null;
        }
        RSAPublicKey pubKey = (RSAPublicKey) key;
        //对长度进行初始化
        StringBuffer sb = new StringBuffer(320);
        sb.append("<RSAKeyValue>")
                .append("<Modulus>")
                .append(Base64Helper.encode(pubKey.getModulus().toByteArray()))
                .append("</Modulus>")
                .append("<Exponent>")
                .append(Base64Helper.encode(pubKey.getPublicExponent().toByteArray()))
                .append("</Exponent>")
                .append("</RSAKeyValue>");
        return sb.toString();
    }
    /**
     *
     * @param xmlKey
     * @return
     * @throws NoSuchAlgorithmException
     * @throws InvalidKeySpecException
     */
    public static PublicKey decodePublicKeyFromXml(String xmlKey)
            throws NoSuchAlgorithmException, InvalidKeySpecException
    {
        xmlKey = xmlKey.replaceAll("\r", "").replaceAll("\n", "");
        //获取 Modulus
        String modulusString = StringHelper.GetMiddleString(xmlKey, "<Modulus>", "</Modulus>");
        BigInteger modulus = new BigInteger(1, Base64Helper.decode(modulusString));
        //获取 Exponent
        String exponentString = StringHelper.GetMiddleString(xmlKey, "<Exponent>", "</Exponent>");
        BigInteger publicExponent = new BigInteger(1, Base64Helper.decode(exponentString));

        //创建公钥对象
        RSAPublicKeySpec rsaPubKey = new RSAPublicKeySpec(modulus, publicExponent);

        KeyFactory keyf = KeyFactory.getInstance("RSA");
        return keyf.generatePublic(rsaPubKey);
    }
    /**
     * 从 Xml 中获取私钥所需要的信息，创建私钥对象。
     * @param xmlKey
     * @return
     * @throws NoSuchAlgorithmException
     * @throws InvalidKeySpecException
     */
    public static PrivateKey decodePrivateKeyFromXml(String xmlKey)
            throws NoSuchAlgorithmException, InvalidKeySpecException
    {
        xmlKey = xmlKey.replaceAll("\r", "").replaceAll("\n", "");

        byte[] modulusBytes = Base64Helper.decode(StringHelper.GetMiddleString(xmlKey, "<Modulus>", "</Modulus>"));
        BigInteger modulus = new BigInteger(1, modulusBytes);

        byte[] exponentBytes = Base64Helper.decode(StringHelper.GetMiddleString(xmlKey, "<Exponent>", "</Exponent>"));
        BigInteger publicExponent = new BigInteger(1,exponentBytes);

        byte[] dBytes = Base64Helper.decode(StringHelper.GetMiddleString(xmlKey, "<D>", "</D>"));
        BigInteger privateExponent = new BigInteger(1, dBytes);

        byte[] pBytes = Base64Helper.decode(StringHelper.GetMiddleString(xmlKey, "<P>", "</P>"));
        BigInteger primeP = new BigInteger(1, pBytes);

        byte[] qBytes = Base64Helper.decode(StringHelper.GetMiddleString(xmlKey, "<Q>", "</Q>"));
        BigInteger primeQ = new BigInteger(1, qBytes);

        byte[] dpBytes = Base64Helper.decode(StringHelper.GetMiddleString(xmlKey, "<DP>", "</DP>"));
        BigInteger primeExponentP = new BigInteger(1, dpBytes);

        byte[] dqBytes = Base64Helper.decode(StringHelper.GetMiddleString(xmlKey, "<DQ>", "</DQ>"));
        BigInteger primeExponentQ = new BigInteger(1, dqBytes);

        byte[] inverseQBytes = Base64Helper.decode(StringHelper.GetMiddleString(xmlKey, "<InverseQ>", "</InverseQ>"));
        BigInteger crtCoefficient = new BigInteger(1, inverseQBytes);

        RSAPrivateCrtKeySpec rsaPriKey = new RSAPrivateCrtKeySpec(modulus,
                publicExponent, privateExponent, primeP, primeQ,
                primeExponentP, primeExponentQ, crtCoefficient);

        KeyFactory keyf = KeyFactory.getInstance("RSA");
        return keyf.generatePrivate(rsaPriKey);
    }
    /**
     * 将私钥中的信息保存为 xml 格式字符串
     * @param key
     * @return
     */
    public static String encodePrivateKeyToXml(PrivateKey key)
    {
        if (!RSAPrivateCrtKey.class.isInstance(key)) {
            return null;
        }
        RSAPrivateCrtKey priKey = (RSAPrivateCrtKey) key;
        //对长度进行初始化
        StringBuffer sb = new StringBuffer(1024);

        sb.append("<RSAKeyValue>")
                .append("<Modulus>")
                .append(Base64Helper.encode(priKey.getModulus().toByteArray()))
                .append("</Modulus>")
                .append("<Exponent>")
                .append(Base64Helper.encode(priKey.getPublicExponent().toByteArray()))
                .append("</Exponent>")
                .append("<P>")
                .append(Base64Helper.encode(priKey.getPrimeP().toByteArray()))
                .append("</P>")
                .append("<Q>")
                .append(Base64Helper.encode(priKey.getPrimeQ().toByteArray()))
                .append("</Q>")
                .append("<DP>")
                .append(Base64Helper.encode(priKey.getPrimeExponentP().toByteArray()))
                .append("</DP>")
                .append("<DQ>")
                .append(Base64Helper.encode(priKey.getPrimeExponentQ().toByteArray()))
                .append("</DQ>")
                .append("<InverseQ>")
                .append(Base64Helper.encode(priKey.getCrtCoefficient().toByteArray()))
                .append("</InverseQ>")
                .append("<D>")
                .append(Base64Helper.encode(priKey.getPrivateExponent().toByteArray()))
                .append("</D>")
                .append("</RSAKeyValue>");
        return sb.toString();
    }
}

